﻿// © Benn Eichhorn 2010
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace Algobox.Feeds.Finance.Chix.CHIXMD
{
    internal static class MessageParser
    {
        static MessageParser()
        {
            Date = DateTime.Today;
        }

        const int MSG_LEN_MIN = 1;
        const int MESSAGETYPE_LOC = 0;
        const int TERMINATOR_LEN = 1;

        // Debug
        const int PLUS_LEN_MIN = 2;
        const int PLUS_TEXT_LOC = 1;

        // Login Accepted
        const int A_LEN = 21;
        const int A_SESSION_LOC = 1;
        const int A_SESSION_LEN = 10;
        const int A_SEQUENCE_LOC = 11;
        const int A_SEQUENCE_LEN = 10;

        // Login Rejected
        const int J_LEN = 2;
        const int J_REASON_LOC = 1;

        // Sequenced Data
        const int S_LEN_MIN = 11;
        public const int S_TIMESTAMP_LOC = 1;
        public const int S_TIMESTAMP_LEN = 8;
        const int S_MESSAGETYPE_LOC = 9;

        // System event
        const int S_S_LEN = 11;
        const int S_S_EVENTCODE_LOC = 10;
                
        // Order Add
        const int S_A_LEN = 43;        

        // Order Add Long        
        const int S_a_LEN = 56;

        // Order Execution
        const int S_E_LEN = 34;

        // Order Execution Long
        const int S_e_LEN = 38;

        // Order Cancel
        const int S_X_LEN = 25;

        // Order Cancel Long
        const int S_x_LEN = 29;

        // Trade - NOT IMPLEMENTED
        const int S_P_LEN = 0;

        // Trade Long - NOT IMPLEMENTED
        const int S_p_LEN = 0;

        // Broken Trade - NOT IMPLEMENTED
        const int S_B_LEN = 0;


        /// <summary>
        /// Todays date
        /// </summary>
        public static readonly DateTime Date;


        internal static bool TryParseSessionMessage(ref string inbound, ref Messages.IMessage message)
        {
            if (inbound.Length < MSG_LEN_MIN)
            {
                message = Messages.Error.GetInstance("TryParseSessionMessage can not parse a " + inbound.Length + " length message", inbound);

                return false;
            }

            char messageType = inbound[MESSAGETYPE_LOC];

            switch (messageType)
            {
                // Sequenced data message
                case 'S':
                    // shouldn't really be called this way
                    // call TryParseSequencedMessage directly
                    return TryParseSequencedMessage(ref inbound, ref message);

                // Heartbeat
                case 'H':
                    message = Messages.Heartbeat.GetInstance();
                    return true;

                // Login accepted
                case 'A':
                    if (inbound.Length != A_LEN)
                    {
                        message = Messages.Error.GetInstance("LoginAccepted failed message length check of " + A_LEN, inbound);
                        return false;
                    }
                    int sequence;
                    if (Int32.TryParse(inbound.Substring(A_SEQUENCE_LOC, A_SEQUENCE_LEN), out sequence))
                    {
                        message = new Messages.LoginAccept(inbound.Substring(A_SESSION_LOC, A_SESSION_LEN), sequence);
                        return true;
                    }
                    else
                    {
                        message = Messages.Error.GetInstance("LoginAccepted failed to parse Sequence as Int32", inbound);
                        return false;
                    }

                // Login rejected
                case 'J':
                    if (inbound.Length != J_LEN)
                    {
                        message = Messages.Error.GetInstance("LoginRejected failed message length check of " + J_LEN, inbound);
                        return false;
                    }
                    message = new Messages.LoginReject(inbound[J_REASON_LOC]);
                    return true;

                // Debug message
                case '+':
                    if (inbound.Length < PLUS_LEN_MIN)
                    {
                        message = Messages.Error.GetInstance("Debug failed message length check of " + PLUS_LEN_MIN, inbound);
                        return false;
                    }
                    message = Messages.Debug.GetInstance();
                    (message as Messages.Debug).Text = inbound.Substring(PLUS_LEN_MIN, inbound.Length - TERMINATOR_LEN);
                    return true;

                default:
                    message = Messages.Error.GetInstance("MessageType " + messageType + " not yet implemented", inbound);
                    return false;
            }
        }
        




        internal static bool TryParseSequencedMessage(ref string inbound, ref Messages.IMessage message)
        {
            // inbound will always be >= 1 which means MESSAGETYPE_LOC will always be there so no need to check for it
            if (inbound[MESSAGETYPE_LOC] == 'H')
            {
                message = Messages.Heartbeat.GetInstance();
                return true;
            }

            if (inbound.Length < S_MESSAGETYPE_LOC)
            {
                message = Messages.Error.GetInstance("Sequenced message failed minimum length check of " + S_MESSAGETYPE_LOC, inbound);
                return false;
            }
            
            switch (inbound[S_MESSAGETYPE_LOC])
            {
                // System Event
                case 'S':
                    if (inbound.Length != S_S_LEN)
                    {
                        message = Messages.Error.GetInstance("SystemEvent failed message length check of " + S_S_LEN, inbound);
                        return false;
                    }
                    message = Messages.SystemEvent.Parse(ref inbound);
                    return (message is Messages.SystemEvent);

                // Order Add
                case 'A':                    
                    if (inbound.Length != S_A_LEN)
                    {
                        message = Messages.Error.GetInstance("OrderAdd failed message length check of " + S_A_LEN, inbound);
                        return false;
                    }
                    message = Messages.OrderAdd.ParseShort(ref inbound);
                    return (message is Messages.OrderAdd);                    
                    

                // Order Add Long
                case 'a':                    
                    if (inbound.Length != S_a_LEN)
                    {
                        message = Messages.Error.GetInstance("OrderAdd failed message length check of " + S_A_LEN, inbound);
                        return false;
                    }
                    message = Messages.OrderAdd.ParseLong(ref inbound);
                    return (message is Messages.OrderAdd);
                    

                case 'E':
                    if (inbound.Length != S_E_LEN)
                    {
                        message = Messages.Error.GetInstance("OrderExecution (short) failed message length check of " + S_E_LEN, inbound);
                        return false;
                    }
                    message = Messages.OrderExecution.ParseShort(ref inbound);
                    return (message is Messages.OrderExecution);


                case 'e':
                    if (inbound.Length != S_e_LEN)
                    {
                        message = Messages.Error.GetInstance("OrderExecution (long) failed message length check of " + S_e_LEN, inbound);
                        return false;
                    }
                    message = Messages.OrderExecution.ParseLong(ref inbound);
                    return (message is Messages.OrderExecution);


                // Order Cancel - S43751039X    49572 1418
                case 'X':
                    if (inbound.Length != S_X_LEN)
                    {
                        message = Messages.Error.GetInstance("OrderCancel (short) failed message length check of " + S_X_LEN, inbound);
                        return false;
                    }
                    message = Messages.OrderCancel.ParseShort(ref inbound);
                    return (message is Messages.OrderCancel);


                case 'x':
                    if (inbound.Length != S_x_LEN)
                    {
                        message = Messages.Error.GetInstance("OrderCancel (long) failed message length check of " + S_x_LEN, inbound);
                        return false;
                    }                    
                    message = Messages.OrderCancel.ParseLong(ref inbound);
                    return (message is Messages.OrderCancel);


                // Trade (short) - Ignored - S49818878P    80298B  1399AGNa       50140      110
                case 'P':
                    //message = Messages.Error.GetInstance("SquencedData MessageType P ignored", inbound);
                    return false;


                // Trade (long) - Ignored
                case 'p':
                    //message = Messages.Error.GetInstance("SquencedData MessageType p ignored", inbound);
                    return false;


                // Broken Trade - Ignored
                case 'B':                    
                    //message = Messages.Error.GetInstance("SquencedData MessageType B ignored", inbound);
                    return false;

                default:
                    message = Messages.Error.GetInstance("SquencedData MessageType " + inbound[S_MESSAGETYPE_LOC] + " not yet implemented", inbound);
                    return false;
            }

        }

    }
}
